﻿// History.cs
//
// Copyright (c) 2007 Mike Strobel
//
// This source code is subject to the terms of the Microsoft Reciprocal License (Ms-RL).
// For details, see <http://www.opensource.org/licenses/ms-rl.html>.
//
// All other rights reserved.

using System;
using System.Collections;
using System.Collections.Generic;

namespace Supremacy.Diplomacy
{
    [Serializable]
    public class History<T> : IList<T> where T : class
    {
        #region Fields
        private readonly Comparison<T> _comparer;
        private T[] _items;
        #endregion

        #region Constructors
        public History(Comparison<T> comparer)
        {
            if (comparer == null)
                comparer = Comparer<T>.Default.Compare;
            _comparer = comparer;
            _items = new T[0];
        }
        #endregion

        #region IList<T> Members
        public int Count
        {
            get { return _items.Length; }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return ((IEnumerable<T>)this).GetEnumerator();
        }

        public void Add(T item)
        {
            if (item == null)
                throw new ArgumentNullException("item");
            lock (_items)
            {
                int insertIndex = _items.Length;
                for (int i = 0; i < _items.Length; i++)
                {
                    if (_comparer(_items[i], item) >= 0)
                    {
                        insertIndex = i;
                        break;
                    }
                }
                InternalInsert(insertIndex, item);
            }
        }

        public bool Contains(T item)
        {
            lock (_items)
            {
                for (int i = 0; i < _items.Length; i++)
                {
                    if (_items[i] == item)
                        return true;
                }
            }
            return false;
        }

        public IEnumerator<T> GetEnumerator()
        {
            return ((IEnumerable<T>)_items).GetEnumerator();
        }

        public bool Remove(T item)
        {
            if (item != null)
            {
                lock (_items)
                {
                    int removeIndex = -1;
                    for (int i = 0; i < _items.Length; i++)
                    {
                        if (_items[i] == item)
                        {
                            removeIndex = i;
                            break;
                        }
                    }
                    if (removeIndex != -1)
                    {
                        RemoveAt(removeIndex);
                        return true;
                    }
                }
            }
            return false;
        }

        public int IndexOf(T item)
        {
            lock (_items)
            {
                for (int i = 0; i < _items.Length; i++)
                {
                    if (_items[i] == item)
                        return i;
                }
            }
            return -1;
        }

        void IList<T>.Insert(int index, T item)
        {
            throw new NotSupportedException("You must add items using the Add method.");
        }

        protected void InternalInsert(int index, T item)
        {
            if (item == null)
                throw new ArgumentNullException("item");
            lock (_items)
            {
                T[] newItems = new T[_items.Length + 1];
                if (index > 0)
                    Array.Copy(_items, 0, newItems, 0, index);
                newItems[index] = item;
                if ((_items.Length - index) > 0)
                    Array.Copy(_items, index, newItems, index + 1, _items.Length - index);
                _items = newItems;
            }
        }

        public void RemoveAt(int index)
        {
            if (index < 0)
                throw new ArgumentOutOfRangeException("index", "value must be >= 0");
            if (index >= _items.Length)
                throw new ArgumentOutOfRangeException("index", "value must be < Count");
            lock (_items)
            {
                T[] newItems = new T[_items.Length - 1];
                if (index > 0)
                    Array.Copy(_items, 0, newItems, 0, index);
                if ((_items.Length - index - 1) > 0)
                    Array.Copy(_items, index + 1, newItems, index, _items.Length - index - 1);
                _items = newItems;
            }
        }

        public T this[int index]
        {
            get { return _items[index]; }
            set { throw new NotSupportedException("You must add items using the Add method."); }
        }

        public void Clear()
        {
            lock (_items)
            {
                _items = new T[0];
            }
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            lock (_items)
            {
                _items.CopyTo(array, arrayIndex);
            }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }
        #endregion

        #region Methods
        public int RemoveWhere(Predicate<T> predicate)
        {
            int result = 0;
            if (predicate != null)
            {
                lock (_items)
                {
                    for (int i = 0; i < _items.Length; i++)
                    {
                        if (predicate(_items[i]) && Remove(_items[i]))
                        {
                            ++result;
                            --i;
                        }
                    }
                }
            }
            return result;
        }
        #endregion
    }
}